module deconv #(
    parameter int unsigned P_ICH = 4,   // Parallel input channels
    parameter int unsigned P_OCH = 4,   // Parallel output channels
    parameter int unsigned N_ICH = 16,  // Total input channels
    parameter int unsigned N_OCH = 16,  // Total output channels
    parameter int unsigned N_IH  = 8,   // Input height
    parameter int unsigned N_IW  = 8,   // Input width
    parameter int unsigned K     = 3,   // Kernel size (KxK)
    parameter int unsigned P     = 1,   // Padding
    parameter int unsigned S     = 2,   // Stride
    parameter int unsigned O_P   = 0,   // Output padding
    parameter int unsigned A_BIT = 8,   // Input activation bit width
    parameter int unsigned W_BIT = 8,   // Weight bit width
    parameter int unsigned B_BIT = 32   // Output bit width (accumulator)
) (
    input logic clk,
    input logic rst_n,

    // Input stream interface
    input  logic [P_ICH*A_BIT-1:0] in_data,
    input  logic                   in_valid,
    output logic                   in_ready,

    // Output stream interface
    output logic [P_OCH*B_BIT-1:0] out_data,
    output logic                   out_valid,
    input  logic                   out_ready,

    // Weight memory interface (ROM)
    output logic [$clog2((N_OCH/P_OCH)*(N_ICH/P_ICH)*K*K)-1:0] weight_addr,
    input  logic [                      P_OCH*P_ICH*W_BIT-1:0] weight_data
);

    // Derived parameters
    localparam int unsigned N_OH = (N_IH - 1) * S + K - 2 * P + O_P;
    localparam int unsigned N_OW = (N_IW - 1) * S + K - 2 * P + O_P;
    localparam int unsigned FOLD_I = N_ICH / P_ICH;
    localparam int unsigned FOLD_O = N_OCH / P_OCH;
    localparam int unsigned KK = K * K;
    localparam int unsigned LB_H = S + 1;

    // Line buffer to store input data
    logic        [P_ICH*A_BIT-1:0] line_buffer[ LB_H] [N_IW][FOLD_I];

    // Accumulator array
    logic signed [      B_BIT-1:0] acc        [P_OCH];

    // State machine
    typedef enum logic [0:0] {
        ST_INIT,  // Initial loading of line buffer
        ST_PROC   // Processing
    } state_t;

    state_t                        state;

    // Counters
    logic   [  $clog2(LB_H+1)-1:0] cntr_init_h;
    logic   [  $clog2(N_IW+1)-1:0] cntr_init_w;
    logic   [$clog2(FOLD_I+1)-1:0] cntr_init_fi;
    logic   [  $clog2(N_OH+1)-1:0] cntr_oh;
    logic   [  $clog2(N_OW+1)-1:0] cntr_ow;
    logic   [$clog2(FOLD_O+1)-1:0] cntr_fo;
    logic   [     $clog2(K+1)-1:0] cntr_kh;
    logic   [     $clog2(K+1)-1:0] cntr_kw;
    logic   [$clog2(FOLD_I+1)-1:0] cntr_fi;
    logic   [  $clog2(N_IH+1)-1:0] ih_to_read;

    // Control signals
    logic                          pipeline_enable;
    logic                          is_fst_kh_kw_fi;
    logic                          is_lst_fi;
    logic                          need_read_input;
    logic                          valid_input_pos;
    logic                          output_ready_to_write;

    // Delayed signals for MAC pipeline
    logic   [     P_ICH*A_BIT-1:0] in_buf_d;
    logic                          is_fst_kh_kw_fi_d;
    logic                          valid_input_pos_d;
    logic                          pipeline_enable_d;
    logic                          output_ready_to_write_dly[P_ICH+1];

    // Calculate if we need to read input
    assign need_read_input = (cntr_oh % S == 0) && (cntr_ow % S == 0) && 
                             (cntr_kh == K - 1) && (cntr_kw == K - 1) && 
                             (cntr_fo == 0) && (ih_to_read < N_IH);

    // Calculate if current position maps to valid input
    logic signed [$clog2(N_OH+K+1)-1:0] h_temp;
    logic signed [$clog2(N_OW+K+1)-1:0] w_temp;
    logic signed [  $clog2(N_IH+1)-1:0] ih;
    logic signed [  $clog2(N_IW+1)-1:0] iw;

    assign h_temp = cntr_oh - cntr_kh + P;
    assign w_temp = cntr_ow - cntr_kw + P;
    assign ih = h_temp / S;
    assign iw = w_temp / S;

    assign valid_input_pos = (h_temp >= 0) && (h_temp % S == 0) && 
                             (w_temp >= 0) && (w_temp % S == 0) &&
                             (ih >= 0) && (ih < N_IH) && 
                             (iw >= 0) && (iw < N_IW);

    assign is_fst_kh_kw_fi = (cntr_kh == K - 1) && (cntr_kw == K - 1) && (cntr_fi == 0);
    assign is_lst_fi = (cntr_fi == FOLD_I - 1);
    assign output_ready_to_write = (cntr_kh == 0) && (cntr_kw == 0) && is_lst_fi;

    // Delay chain for output_ready_to_write to match total latency (1 + P_ICH cycles)
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (int i = 0; i <= P_ICH; i++) begin
                output_ready_to_write_dly[i] <= 1'b0;
            end
        end else if (pipeline_enable) begin
            output_ready_to_write_dly[0] <= output_ready_to_write;
            for (int i = 1; i <= P_ICH; i++) begin
                output_ready_to_write_dly[i] <= output_ready_to_write_dly[i-1];
            end
        end
    end

    // Pipeline enable control
    assign pipeline_enable = (state == ST_PROC) && (!output_ready_to_write_dly[P_ICH] || out_ready);

    // Input ready control
    assign in_ready        = ((state == ST_INIT) || (state == ST_PROC && need_read_input)) && in_valid;

    // Weight address calculation (reversed kernel indices)
    logic [$clog2(K+1)-1:0] kh_rev, kw_rev;
    assign kh_rev      = K - 1 - cntr_kh;
    assign kw_rev      = K - 1 - cntr_kw;
    assign weight_addr = (cntr_fo * FOLD_I * KK) + (cntr_fi * KK) + (kh_rev * K) + kw_rev;

    // State machine and main control logic
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state        <= ST_INIT;
            cntr_init_h  <= 0;
            cntr_init_w  <= 0;
            cntr_init_fi <= 0;
            cntr_oh      <= 0;
            cntr_ow      <= 0;
            cntr_fo      <= 0;
            cntr_kh      <= K - 1;
            cntr_kw      <= K - 1;
            cntr_fi      <= 0;
            ih_to_read   <= S;

            // Clear line buffer
            for (int h = 0; h < LB_H; h++) begin
                for (int w = 0; w < N_IW; w++) begin
                    for (int fi = 0; fi < FOLD_I; fi++) begin
                        line_buffer[h][w][fi] <= '0;
                    end
                end
            end

        end else begin
            case (state)
                ST_INIT: begin
                    // Load initial S rows of input data into line buffer
                    if (in_valid) begin
                        line_buffer[cntr_init_h][cntr_init_w][cntr_init_fi] <= in_data;

                        if (cntr_init_fi == FOLD_I - 1) begin
                            cntr_init_fi <= 0;
                            if (cntr_init_w == N_IW - 1) begin
                                cntr_init_w <= 0;
                                if (cntr_init_h == S - 1) begin
                                    state <= ST_PROC;
                                end else begin
                                    cntr_init_h <= cntr_init_h + 1;
                                end
                            end else begin
                                cntr_init_w <= cntr_init_w + 1;
                            end
                        end else begin
                            cntr_init_fi <= cntr_init_fi + 1;
                        end
                    end
                end

                ST_PROC: begin
                    if (pipeline_enable) begin
                        // Load new input data if needed
                        if (need_read_input && in_valid) begin
                            line_buffer[ih_to_read%LB_H][cntr_ow/S][cntr_fi] <= in_data;
                            if ((cntr_ow / S == N_IW - 1) && (cntr_fi == FOLD_I - 1)) begin
                                ih_to_read <= ih_to_read + 1;
                            end
                        end

                        // Counter updates
                        if (cntr_fi == FOLD_I - 1) begin
                            cntr_fi <= 0;
                            if (cntr_kw == 0) begin
                                cntr_kw <= K - 1;
                                if (cntr_kh == 0) begin
                                    cntr_kh <= K - 1;
                                    if (cntr_fo == FOLD_O - 1) begin
                                        cntr_fo <= 0;
                                        if (cntr_ow == N_OW - 1) begin
                                            cntr_ow <= 0;
                                            if (cntr_oh == N_OH - 1) begin
                                                // Done with one complete deconv, go back to init
                                                state        <= ST_INIT;
                                                cntr_oh      <= 0;
                                                cntr_init_h  <= 0;
                                                cntr_init_w  <= 0;
                                                cntr_init_fi <= 0;
                                                ih_to_read   <= S;
                                            end else begin
                                                cntr_oh <= cntr_oh + 1;
                                            end
                                        end else begin
                                            cntr_ow <= cntr_ow + 1;
                                        end
                                    end else begin
                                        cntr_fo <= cntr_fo + 1;
                                    end
                                end else begin
                                    cntr_kh <= cntr_kh - 1;
                                end
                            end else begin
                                cntr_kw <= cntr_kw - 1;
                            end
                        end else begin
                            cntr_fi <= cntr_fi + 1;
                        end
                    end
                end
            endcase
        end
    end

    // Delay registers for input data and control signals to match weight_data latency
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            in_buf_d          <= '0;
            is_fst_kh_kw_fi_d <= 1'b0;
            valid_input_pos_d <= 1'b0;
            pipeline_enable_d <= 1'b0;
        end else begin
            if (valid_input_pos) begin
                in_buf_d <= line_buffer[ih%LB_H][iw][cntr_fi];
            end else begin
                in_buf_d <= '0;
            end
            is_fst_kh_kw_fi_d <= is_fst_kh_kw_fi;
            valid_input_pos_d <= valid_input_pos;
            pipeline_enable_d <= pipeline_enable;
        end
    end

    // MAC array instances for each output channel
    logic        [A_BIT-1:0] x_vec[P_ICH];
    logic signed [W_BIT-1:0] w_vec[P_OCH] [P_ICH];

    // Unpack delayed input data to array
    always_comb begin
        for (int i = 0; i < P_ICH; i++) begin
            x_vec[i] = in_buf_d[i*A_BIT+:A_BIT];
        end
    end

    // Unpack weight data to array
    always_comb begin
        for (int o = 0; o < P_OCH; o++) begin
            for (int i = 0; i < P_ICH; i++) begin
                w_vec[o][i] = weight_data[(P_ICH*o+i)*W_BIT+:W_BIT];
            end
        end
    end

    // Instantiate MAC arrays for each output channel
    generate
        for (genvar o = 0; o < P_OCH; o++) begin : gen_mac_array
            mac_array #(
                .P_ICH(P_ICH),
                .A_BIT(A_BIT),
                .W_BIT(W_BIT),
                .B_BIT(B_BIT)
            ) u_mac_array (
                .clk  (clk),
                .rst_n(rst_n),
                .en   (pipeline_enable_d && valid_input_pos_d),
                .clr  (is_fst_kh_kw_fi_d),
                .x_vec(x_vec),
                .w_vec(w_vec[o]),
                .acc  (acc[o])
            );
        end
    endgenerate

    // Output generation
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            out_data  <= '0;
            out_valid <= 1'b0;
        end else begin
            if (output_ready_to_write_dly[P_ICH] && (!out_valid || out_ready)) begin
                // Pack accumulator outputs
                for (int o = 0; o < P_OCH; o++) begin
                    out_data[o*B_BIT+:B_BIT] <= acc[o];
                end
                out_valid <= 1'b1;
            end else if (out_ready) begin
                out_valid <= 1'b0;
            end
        end
    end

endmodule
